"""Instrumentation command wiring."""

from __future__ import annotations

import argparse
import difflib
import json
from pathlib import Path

from flowmap_core.instrumentation.base import InstrumentationConfig
from flowmap_core.instrumentation.gptl import get_instrumenter
from flowmap_core.instrumentation.platforms import platform_registry
from flowmap_core.analyzers import analyzer_registry


SUPPORTED_LANGUAGES = ["fortran", "cpp", "c++"]


def register(subparsers: argparse._SubParsersAction) -> None:
    parser = subparsers.add_parser("instrument", help="Insert performance probes into source files")
    parser.add_argument("files", nargs="+", help="Source files to instrument")
    parser.add_argument("--language", choices=SUPPORTED_LANGUAGES, required=True, help="Language of the input sources")
    parser.add_argument("--profile", default="gptl", choices=platform_registry.list_profiles(),
                       help="Instrumentation profile/platform to use")
    parser.add_argument("--apply", action="store_true", help="Write changes back to disk")
    parser.add_argument("--include", nargs="*", help="Only instrument the listed functions/subroutines")
    parser.add_argument("--exclude", nargs="*", help="Skip the listed functions/subroutines")
    parser.add_argument("--analysis-file", type=Path, help="JSON analysis file to guide function selection")
    parser.add_argument("--auto-select", choices=["all", "large", "entry-points", "hot-path"],
                       help="Automatically select functions based on analysis")
    parser.add_argument("--min-lines", type=int, default=5,
                       help="Minimum function size for auto-selection (for 'large' mode)")
    parser.add_argument("--show-profile", action="store_true",
                       help="Show detailed information about the selected profile")
    parser.add_argument("--list-profiles", action="store_true",
                       help="List all available platform profiles")
    parser.set_defaults(handler=handle)


def handle(args: argparse.Namespace) -> int:
    # Handle informational requests
    if args.list_profiles:
        print("Available Platform Profiles:")
        print("=" * 40)
        for profile_name in platform_registry.list_profiles():
            profile = platform_registry.get_profile(profile_name)
            print(f"{profile_name:12} - {profile.description}")
        return 0

    if args.show_profile:
        print(platform_registry.get_profile_info(args.profile))
        return 0

    # Handle function auto-selection from analysis
    include_functions = args.include or []
    exclude_functions = args.exclude or []

    if args.analysis_file and args.auto_select:
        selected_functions = _load_and_select_functions(
            args.analysis_file, args.auto_select, args.language, args.min_lines
        )
        if selected_functions:
            include_functions.extend(selected_functions)
            print(f"[instrument] Auto-selected {len(selected_functions)} functions based on analysis")

    config = InstrumentationConfig(
        profile=args.profile,
        language=args.language,
        apply=args.apply,
        include_functions=include_functions if include_functions else None,
        exclude_functions=exclude_functions if exclude_functions else None,
    )

    instrumenter = get_instrumenter(args.language, args.profile)

    status = 0
    for file_path in args.files:
        path = Path(file_path)
        if not path.exists():
            print(f"[instrument] File not found: {path}")
            status = 1
            continue

        patch = instrumenter.plan(path, config)
        if not patch.has_changes():
            print(f"[instrument] No changes for {path}")
            continue

        if args.apply:
            path.write_text(patch.modified)
            print(f"[instrument] Applied instrumentation to {path}")
        else:
            print(f"[instrument] Preview for {path} (use --apply to write):")
            diff = difflib.unified_diff(
                patch.original.splitlines(),
                patch.modified.splitlines(),
                fromfile=str(path),
                tofile=f"{path} (instrumented)",
                lineterm="",
            )
            for line in diff:
                print(line)

    return status


def _load_and_select_functions(analysis_file: Path, selection_mode: str, language: str, min_lines: int) -> list[str]:
    """Load analysis results and select functions based on criteria."""
    try:
        with open(analysis_file, 'r') as f:
            analysis_data = json.load(f)
    except Exception as e:
        print(f"[instrument] Error loading analysis file: {e}")
        return []

    # Get analysis data for the specified language
    language_data = analysis_data.get(language)
    if not language_data:
        print(f"[instrument] No analysis data found for language: {language}")
        return []

    functions = language_data.get('functions', [])
    call_graph = language_data.get('call_graph', {}).get('edges', {})

    selected = []

    if selection_mode == "all":
        selected = [f['name'] for f in functions]

    elif selection_mode == "large":
        selected = [
            f['name'] for f in functions
            if f.get('line_count', 0) >= min_lines
        ]

    elif selection_mode == "entry-points":
        # Functions not called by others (entry points)
        all_callees = set()
        for callees in call_graph.values():
            all_callees.update(callees)

        function_names = {f['full_name'] for f in functions}
        entry_points = function_names - all_callees
        selected = [f['name'] for f in functions if f['full_name'] in entry_points]

    elif selection_mode == "hot-path":
        # Functions with high fan-out (call many others) or are called frequently
        call_counts = {}
        for caller, callees in call_graph.items():
            call_counts[caller] = call_counts.get(caller, 0) + len(callees)

        callee_counts = {}
        for callees in call_graph.values():
            for callee in callees:
                callee_counts[callee] = callee_counts.get(callee, 0) + 1

        # Select functions with high call counts or that are called frequently
        hot_functions = set()
        for func in functions:
            full_name = func['full_name']
            fan_out = call_counts.get(full_name, 0)
            called_count = callee_counts.get(full_name, 0)

            # Consider a function "hot" if it calls many others or is called frequently
            if fan_out >= 3 or called_count >= 3:
                hot_functions.add(func['name'])

        selected = list(hot_functions)

    print(f"[instrument] Selected functions using '{selection_mode}' criteria: {selected}")
    return selected
